有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java使用信号量从2个线程打印可选输出

我正在学习信号量和多线程的使用,但我有点卡住了。我有两个线程分别打印G和H,我的目标是交替每个线程的输出,这样输出字符串如下

G
H
G
H
G
H

这两个类的布局都与下面的类似

public class ClassA extends Thread implements Runnable{

     Semaphore semaphore = null;
     public ClassA(Semaphore semaphore){

         this.semaphore = semaphore;
     }

     public void run() {

        while(true)
        {
            try{
                semaphore.acquire();
                for(int i=0; i<1000; i++){
                    System.out.println("F");

                }
                Thread.currentThread();
                Thread.sleep(100);
            }catch(Exception e)
            {
                System.out.println(e.toString());
            }
            semaphore.release();
        }

    }

}

下面是我的主要课程

public static void main(String[] args) throws InterruptedException {

    Semaphore semaphore = new Semaphore(1);

    ClassA clasA = new ClassA(semaphore);
    Thread t1 = new Thread(clasA);
    ClassB clasB = new ClassB(semaphore);
    Thread t2 = new Thread(clasB);
    t1.start();
    t2.join();
    t2.start();

我得到的结果与我预期的结果相差太大。谁能帮帮我吗?我是不是误用了信号灯?有什么帮助吗


共 (2) 个答案

  1. # 1 楼答案

    正如其他人所指出的,锁本身并不强制执行任何顺序,而且,您无法确定线程何时启动(调用Thread.start()将在将来的某个时间启动线程,但这可能需要一段时间)

    但是,您可以使用锁(比如Semaphore)来强制执行命令。在这种情况下,可以使用两个信号量来打开和关闭线程(备用)。这两个线程(或Runnables)确实需要提前了解彼此——线程可以“加入”聚会的更动态的方法会更复杂

    下面是一个具有可重复结果的可运行示例类(在测试多线程时,这总是一件好事)。我会让你自己去弄清楚为什么会这样

    import java.util.concurrent.*;
    
    public class AlternateSem implements Runnable {
    
        static final CountDownLatch DONE_LATCH = new CountDownLatch(2);
        static final int TIMEOUT_MS = 1000;
        static final int MAX_LOOPS = 10; 
    
        public static void main(String[] args) {
    
            ExecutorService executor = Executors.newCachedThreadPool();
            try {
                AlternateSem as1 = new AlternateSem(false);
                AlternateSem as2 = new AlternateSem(true);
                as1.setAlternate(as2);
                as2.setAlternate(as1);
                executor.execute(as1);
                executor.execute(as2);
                if (DONE_LATCH.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
                    System.out.println();
                    System.out.println("Done");
                } else {
                    System.out.println("Timeout");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                executor.shutdownNow();
            }
        }
    
        final Semaphore sem = new Semaphore(0);
        final boolean odd;
        AlternateSem other;
    
        public AlternateSem(boolean odd) { 
            this.odd = odd;
        }
    
        void setAlternate(AlternateSem other) { this.other = other; }
        void release() { sem.release(); }
        void acquire() throws Exception { sem.acquire(); }
    
        @Override
        public void run() {
    
            if (odd) {
                other.release();
            }
            int i = 0;
            try {
                while (i < MAX_LOOPS) { 
                    i++;
                    other.acquire();
                    System.out.print(odd ? "G " : "H ");
                    release();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            DONE_LATCH.countDown();
        }
    }
    
  2. # 2 楼答案

    信号量不能帮助你解决这样的任务

    据我所知,JVM不保证线程执行的任何顺序。这意味着,如果运行多个线程,一个线程可以连续执行多次,并且处理器时间比其他任何线程都多。因此,如果你想让你的线程按照特定的顺序执行,你可以,举个最简单的例子,创建一个静态布尔变量,它将扮演你线程的切换器的角色。使用wait()和notify()方法将是更好的方法,而Interface Condition将是我认为最好的方法

    import java.io.IOException;
    
    public class Solution {
        public static boolean order;
    
        public static void main(String[] args) throws IOException, InterruptedException {
            Thread t1 = new ThreadPrint("G", true);
            Thread t2 = new ThreadPrint("O", false);
            t1.start();
            t2.start();
            t2.join();
    
            System.out.println("Finish");
        }
    
    }
    
    class ThreadPrint extends Thread {
    
        private String line;
        private boolean order;
    
        public ThreadPrint(String line, boolean order) {
            this.line = line;
            this.order = order;
        }
    
        @Override
        public void run() {
            int z = 0;
            while (true) {
                try {
                    for (int i = 0; i < 10; i++) {
                        if (order == Solution.order) {
                            System.out.print(line + " ");
                            Solution.order = !order;
                        }
                    }
                    sleep(100);
                } catch (Exception e) {
                    System.out.println(e.toString());
                }
            }
        }
    }
    

    顺便说一句,系统可能会出现另一个问题。out通常是一个操作系统缓冲区,您的操作系统可以自己按顺序输出消息

    另外,您不应该同时继承线程和实现Runnable

    public class ClassA extends Thread implements Runnable{
    

    因为Thread类已经实现了Runnable。你只能选择一种更适合你的方式

    你应该先开始一个线程,然后加入它,而不是反过来

    t1.start();
    t2.join();
    t2.start();